package com.wang.pcm_wav;

import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

/**
 * 音频文件转换工具类，不需要实例化，直接使用静态方法进行转换。 目前提供PCM到WAV格式转换。
 * 
 * @author wang
 *
 */
public class Convertor {
	private Convertor() {
	}

	/**
	 * 将PCM原始文件转换为WAV格式
	 * 
	 * @param numChannels
	 *            通道数
	 * @param sampleRate
	 *            采样率
	 * @param bitsPerSample
	 *            位数
	 * @param src
	 *            原始文件
	 * @param target
	 *            目标文件
	 * @return 转换成功返回0，失败返回-1。
	 * @throws IOException
	 *             如果文件不存在，或读写错误，抛出异常。
	 */
	public static int pcm2wav(short numChannels, int sampleRate, short bitsPerSample, String src, String target)
			throws IOException {
		FileInputStream fis = null;
		FileOutputStream fos = null;
		try {
			fis = new FileInputStream(src);
			fos = new FileOutputStream(target);

			ByteArrayOutputStream baos = new ByteArrayOutputStream();
			byte[] buf = new byte[1024];
			int len;
			while (-1 != (len = fis.read(buf))) {
				baos.write(buf, 0, len);
			}

			WaveHelper helper = new WaveHelper(numChannels, sampleRate, bitsPerSample, baos.size());
			byte[] h = helper.getHeader();
			if (null == h) {
				return -1;
			}
			// write header
			fos.write(h);
			// write data stream
			fos.write(baos.toByteArray());
			return 0;
		} finally {
			if (null != fis) {
				fis.close();
			}
			if (null != fos) {
				fos.close();
			}
		}
	}

	/**
	 * 辅助类，主要用于生成WAV文件头数据。
	 * 
	 * @author wang
	 *
	 */
	private static class WaveHelper {
		private String chunkId = "RIFF";
		private int chunkSize;
		private String format = "WAVE";
		private String subChunk1Id = "fmt ";
		private int subChunk1Size = 16;
		private short audioFormat = 1;
		private short numChannels;
		private int sampleRate;
		private int byteRate;
		private short blockAlign;
		private short bitsPerSample;
		private String subChunk2Id = "data";
		private int subChunk2Size;

		/**
		 * 
		 * @param numChannels
		 *            通道数
		 * @param sampleRate
		 *            采样率
		 * @param bitsPerSample
		 *            位数
		 * @param PCMSize
		 *            原始PCM文件大小
		 */
		public WaveHelper(short numChannels, int sampleRate, short bitsPerSample, int PCMSize) {
			this.numChannels = numChannels;
			this.sampleRate = sampleRate;
			this.bitsPerSample = bitsPerSample;
			this.blockAlign = (short) (this.bitsPerSample * this.numChannels / 8);
			this.byteRate = this.sampleRate * this.blockAlign;
			this.subChunk2Size = PCMSize;
			this.chunkSize = this.subChunk2Size + (44 - 8);
		}

		/**
		 * 返回wav文件头字节数组，如果异常返回NULL
		 * 
		 * @return 文件头字节数组
		 */
		public byte[] getHeader() {
			ByteArrayOutputStream baos = new ByteArrayOutputStream();
			DataOutputStream dos = new DataOutputStream(baos);
			try {
				dos.write(this.chunkId.getBytes());
				dos.writeInt(Integer.reverseBytes(this.chunkSize));
				dos.write(this.format.getBytes());
				dos.write(this.subChunk1Id.getBytes());
				dos.writeInt(Integer.reverseBytes(this.subChunk1Size));
				dos.writeShort(Short.reverseBytes(this.audioFormat));
				dos.writeShort(Short.reverseBytes(this.numChannels));
				dos.writeInt(Integer.reverseBytes(this.sampleRate));
				dos.writeInt(Integer.reverseBytes(this.byteRate));
				dos.writeShort(Short.reverseBytes(this.blockAlign));
				dos.writeShort(Short.reverseBytes(this.bitsPerSample));
				dos.write(this.subChunk2Id.getBytes());
				dos.writeInt(Integer.reverseBytes(this.subChunk2Size));
			} catch (IOException e) {
				e.printStackTrace();
				return null;
			}
			return baos.toByteArray();
		}
	}
}
